package com.laomo.lib_bt;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import androidx.annotation.NonNull;

import com.jieli.jl_bt_ota.interfaces.IUpgradeCallback;
import com.jieli.jl_bt_ota.model.base.BaseError;
import com.laomo.lib_bt.callback.CharacteristicCallbackInterface;
import com.laomo.lib_bt.callback.ConnectCallbackInterface;
import com.laomo.lib_bt.callback.OtaAvailableCallbackInterface;
import com.laomo.lib_bt.callback.ScanCallbackInterface;
import com.laomo.lib_bt.ota.OTAUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/**
 * LibBtPlugin
 */
public class LibBtPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, ScanCallbackInterface, OtaAvailableCallbackInterface, IUpgradeCallback, ConnectCallbackInterface , CharacteristicCallbackInterface {
    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity
    private MethodChannel channel;
    private Handler mHandler = new Handler();
    private Context mContext;
    private BleManager mBleManager;
    private Activity activity;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "lib_bt");
        channel.setMethodCallHandler(this);
        mContext = flutterPluginBinding.getApplicationContext();
        mBleManager = BleManager.getInstance(mContext);
        mBleManager.addScanCallbackListener(this);
        mBleManager.addConnectCallbackListener(this);
        mBleManager.addCharacteristicCallbackListener(this);
        OTAUtil.getInstance(mContext).addOtaAvailableCallbackListener(this);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        Map<String, Object> args;
        String address;
        String serverUuid;
        String uuid;
        Map<String, Object> map;

        switch (call.method) {
            case "startOta":
                args = (Map<String, Object>) call.arguments;
                address = (String) args.get("address");
                String strOtaData = (String) args.get("data");
                byte[] otaData = Base64.decode(strOtaData, Base64.DEFAULT);
                OTAUtil.getInstance(mContext).startOta(address, otaData, this);
                break;
            case "getPlatformVersion":
                result.success("Android " + android.os.Build.VERSION.RELEASE);
                break;
            case "startScan":
                args = (Map<String, Object>) call.arguments;
                int timeout = (Integer) args.get("timeout");
                mBleManager.startScan(timeout);
                result.success("");
                break;
            case "stopScan":
                BleManager.getInstance(mContext).stopScan();
                break;
            case "connect":
                mBleManager.connect(call.arguments.toString());
                break;
            case "disconnect":
                mBleManager.disconnect(call.arguments.toString());
                break;
            case "write":
                args = (Map<String, Object>) call.arguments;
                address = (String) args.get("address");
                serverUuid = (String) args.get("serviceUuid");
                uuid = (String) args.get("uuid");
                String strData = (String) args.get("data");
                byte[] data = Base64.decode(strData, Base64.DEFAULT);
                mBleManager.write(address, serverUuid, uuid, data);
                break;
            case "openBle":
                openBle();
                break;
            case "openGps":
                openGps();
                break;
            case "isBleEnable":
                map = new HashMap<>();
                map.put("isBleEnable", mBleManager.isBluetoothEnable());
                result.success(map);
                break;
            case "isGpsEnable":
                map = new HashMap<>();
                map.put("isGpsEnable", isGpsOpen(mContext));
                result.success(map);
                break;
            case "setUUIDInfo":
                mBleManager.setUUIDInfo((Map<String, String>) call.arguments);
                break;
            case "isOTA":
                result.success(OTAUtil.getInstance(mContext).isOTA());
                break;
            default:
                result.notImplemented();
        }

    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }

    @Override
    public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
        activity = binding.getActivity();
    }

    @Override
    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
    }

    @Override
    public void onDetachedFromActivity() {
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {
    }

    public void openGps() {
        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        activity.startActivityForResult(intent, 2);
    }

    /**
     * 打开蓝牙
     */
    @SuppressLint("MissingPermission")
    public void openBle() {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        activity.startActivityForResult(enableBtIntent, 1);
    }

    public static boolean isGpsOpen(final Context context) {
        int locationMode = 0;
        String locationProviders;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            try {
                locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
                return false;
            }
            return locationMode != Settings.Secure.LOCATION_MODE_OFF;
        } else {
            locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
            return !TextUtils.isEmpty(locationProviders);
        }

    }

    private void invokeMethod(String methodName) {
        invokeMethod(methodName, "");
    }

    private void invokeMethod(String methodName, Object arg) {
        mHandler.post(() -> {
            channel.invokeMethod(methodName, arg);
        });
    }

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        BluetoothDevice device = result.getDevice();
        byte[] bytes = result.getScanRecord().getBytes();
        String name = device.getName();

        Map<String, Object> map = new HashMap<>();
        map.put("bytes", bytes);
        map.put("name", name);
        map.put("address", device.getAddress());
        map.put("rssi", result.getRssi());
        invokeMethod("onScanResult", map);
    }

    @Override
    public void onStartScan() {
        invokeMethod("onStartScan");
    }

    @Override
    public void onStopScan() {
        invokeMethod("onStopScan");
    }

    @Override
    public void onOtaAvailable(BluetoothDevice device, boolean isMandatoryUpgrade) {
        Map<String, Object> map = new HashMap<>();
        map.put("address", device.getAddress());
        map.put("mac", device.getAddress().replace(":","").toLowerCase());
        map.put("isMandatoryUpgrade", isMandatoryUpgrade);
        invokeMethod("onOtaAvailable", map);
    }


    @Override
    public void onStartOTA() {
        invokeMethod("onOtaStart");
    }

    @Override
    public void onNeedReconnect(String s, boolean b) {
        Log.d("BleManager", "onNeedReconnect s:" + s);
    }

    @Override
    public void onProgress(int i, float v) {
        Log.d("BleManager", "onProgress: type:" + i + ",progress:" + v);
        if (i == 0) {
            invokeMethod("onOtaBootLoaderProgress", (int) v);
        } else {
            invokeMethod("onOtaProgress", (int) v);
        }
    }

    @Override
    public void onStopOTA() {
        invokeMethod("onOtaSuccess");
    }

    @Override
    public void onCancelOTA() {
        invokeMethod("onOtaFail", "onCancelOTA");
    }

    @Override
    public void onError(BaseError baseError) {
        Log.d("BleManager", "onError :" + baseError.toString());
        invokeMethod("onOtaFail", baseError.toString());
    }

    @Override
    public void onConnecting(String address, BluetoothDevice device) {
        invokeMethod("onConnecting", address);
    }

    @Override
    public void onMtuChanged(String address, BluetoothGatt gatt, int mtu, int status) {
        Map<String, Object> map = new HashMap<>();
        map.put("address", address);
        map.put("mtu", mtu);
        invokeMethod("onMtuChanged", map);
    }

    @Override
    public void onConnectSuccess(String address, BluetoothGatt gatt) {
        invokeMethod("onConnectSuccess", address);
    }

    @Override
    public void onConnectFailed(String address, BluetoothDevice device) {
        invokeMethod("onConnectFailed", address);
    }

    @Override
    public void onDisConnect(String address, BluetoothGatt gatt) {
        invokeMethod("onDisConnect", address);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) {
        Map<String, Object> map = new HashMap<>();
        map.put("address", gatt.getDevice().getAddress());
        map.put("uuid", characteristic.getUuid().toString());
        map.put("data", value);
        invokeMethod("onData", map);
    }
}
